/**
 * Copyright(c) 2013 Jade Techonologies Co., Ltd.
 *
 * History:
 *   13-4-28 下午1:38 Created by dwzhang
 */
package com.jade.framework.startup.def;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

import com.jade.framework.base.JadeSystem;
import com.jade.framework.base.net.InetAddressUtils;
import com.jade.framework.startup.Constants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 系统定义。
 *
 * @author dwzhang
 * @version 1.0 13-4-28 下午1:38
 */
public class SystemDefinition
{
    private static SystemDefinition instance = new SystemDefinition();

    /** 内部标识符 */
    private String id;

    /** 系统名称. */
    private String name;

    /** 组播地址 */
    private InetAddress multicast;

    /** 组播端口 */
    private int multicastPort = Constants.DEFAULT_UDP_PORT;

    protected Log logger = LogFactory.getLog(SystemDefinition.class);

    private SortedMap<String, HostDefinition> hosts = new TreeMap<String, HostDefinition>();
    private SortedMap<String, ServerDefinition> servers = new TreeMap<String, ServerDefinition>();

    private HostDefinition host = null;
    private ServerDefinition server = null;

    /**
     * 返回实例
     *
     * @return 实例
     */
    public static final SystemDefinition getInstance()
    {
        return instance;
    }

    private SystemDefinition()
    {
    }

    /**
     * 返回对象名称
     *
     * @return 对象名称
     */
    public String getName()
    {
        return name;
    }

    /**
     * 返回系统标示符，为了能够标识在一个网络内的不同系统，而产生的一个标识。
     * 如果系统没有设置名称，那么返回的是这个标识
     *
     * @return 系统标识
     */
    public String getId()
    {
        if (id == null) {
            id = "system_" + getInternalId();
        }
        return id;
    }

    /**
     * 返回内部标识号，根据局域网内目前所有的非127.0.0.1的IP地址的最后一位之和，除以256之后获得的数字
     *
     * @return 内部标识号
     */
    private int getInternalId()
    {
        int c = 0;
        if (!hosts.isEmpty()) {
            Iterator values = hosts.values().iterator();
            HostDefinition def = null;
            String address = null;
            while (values.hasNext()) {
                def = (HostDefinition)values.next();
                address = def.getAddress();
                //如果是127.0.0.1，则采用主机名对应的IP地址作为因子
                if ("127.0.0.1".equals(address) && JadeSystem.getHostName().equals(def.getName())) {
                    try {
                        InetAddress addr = InetAddress.getByName(JadeSystem.getHostName());
                        address = addr.getHostAddress();
                    }
                    catch (UnknownHostException e) {
                        logger.warn("unknown host:", e);
                    }
                }
                c += getIdFromAddress(address);
            }
        }
        else {
            c = getIdFromAddress(InetAddressUtils.getLocalAddress().getHostAddress());
        }
        return c & 0xFF;
    }

    private int getIdFromAddress(String address)
    {
        int i = address.lastIndexOf('.');
        return Integer.parseInt(address.substring(i + 1));
    }

    private static final String DEFAULT_MULTICAST_PREFIX = "228.8.8.";

    /**
     * 返回组播通讯地址
     * <p/>
     * 组播通讯地址是为了能够让每一个应用服务器能够自动的被探测和识别，
     * 并且相互直接能够进行通讯而与系统固定的一个地址，
     * 按照D类地址的规则，组播地址可以是<CODE>224.0.0.0</CODE> 到 <CODE>239.255.255.255</CODE>
     * 为了简化系统的部署，在没有设置广播地址的情况下，我们根据系统中主机的IP地址规则来自动产生一个
     * 地址，从 228.8.8.0 - 228.8.8.255。如果自己设置，那么设置为 227.7.7.0 - 227.7.7.255之间
     * </p>
     */
    public InetAddress getMulticastAddress()
    {
        if (multicast == null) {
            try {
                multicast = InetAddress.getByName(DEFAULT_MULTICAST_PREFIX + getInternalId());
            }
            catch (UnknownHostException e) {
                logger.warn("unknown host:", e);
            }
        }
        return multicast;
    }

    /**
     * 返回组播端口，允许所有的服务器采用同样的端口
     *
     * @return 组播端口
     */
    public int getMulticastPort()
    {
        return multicastPort;
    }

    /**
     * 返回所有的主机
     *
     * @return 所有的主机
     */
    public HostDefinition[] getHosts()
    {
        Set<String> keys = hosts.keySet();
        HostDefinition[] defs = new HostDefinition[keys.size()];
        int i = 0;
        for (String key : keys) {
            defs[i] = hosts.get(key);
            i++;
        }
        return defs;
    }

    /**
     * 返回所有拥有相应服务器类型的主机
     *
     * @return 所有的主机
     */
    public HostDefinition[] getHostsByServer(String server)
    {
        Set<String> keys = hosts.keySet();
        int size = keys.size();
        ArrayList<HostDefinition> list = new ArrayList<HostDefinition>(size);
        HostDefinition host;
        for (String key : keys) {
            host = (HostDefinition)hosts.get(key);
            if (host.getServer(server) != null) {
                list.add(host);
            }
        }
        HostDefinition[] hosts = new HostDefinition[list.size()];
        list.toArray(hosts);
        return hosts;
    }

    /**
     * 添加主机
     *
     * @param host 主机
     */
    public void addHost(HostDefinition host)
    {
        hosts.put(host.getName(), host);
    }

    /**
     * 根据主机名返回主机
     *
     * @param name 主机名
     * @return 主机
     */
    public HostDefinition getHost(String name)
    {
        return hosts.get(name);
    }

    /**
     * 添加服务器
     *
     * @param server 服务器
     */
    public void addServer(ServerDefinition server)
    {
        servers.put(server.getType(), server);
    }

    /**
     * 返回所有的服务器定义
     *
     * @return 所有的服务器定义
     */
    public ServerDefinition[] getServers()
    {
        Set<String> keys = servers.keySet();
        ServerDefinition[] defs = new ServerDefinition[keys.size()];
        int i = 0;
        for (String key : keys) {
            defs[i] = servers.get(key);
            i++;
        }
        return defs;
    }

    /**
     * 根据服务器类型返回服务器定义
     *
     * @param type 服务器类型
     * @return 服务器定义
     */
    public ServerDefinition getServer(String type)
    {
        return servers.get(type);
    }

    /**
     * 返回当前服务器定义
     *
     * @return 服务器定义
     */
    public ServerDefinition getCurrentServer()
    {
        if (server == null) {
            HostDefinition host = getCurrentHost();
            if (host != null) {
                String serverType = System.getProperty("jade.system.type");
                server = host.getServer(serverType);
            }
        }
        return server;
    }

    /**
     * 返回当前主机定义
     *
     * @return 主机定义
     */
    public HostDefinition getCurrentHost()
    {
        if (host == null) {
            String hostName = System.getProperty("jade.system.host");
            host = getHost(hostName);
        }
        return host;
    }

    /**
     * 设置系统标识符
     *
     * @param id 标识符
     */
    public void setId(String id)
    {
        this.id = id;
    }

    /**
     * 设置
     *
     * @param multicast
     * @throws UnknownHostException
     */
    public void setMulticastAddress(String multicast)
            throws UnknownHostException
    {
        InetAddress addr = InetAddress.getByName(multicast);
        if (!addr.isMulticastAddress()) {
            throw new IllegalArgumentException("Invalid multicast address:" + multicast +
                                               ", Please set address like: 227.7.7.7 [227.7.7.0 - 227.7.7.255]");
        }
        this.multicast = addr;
    }

    /**
     * 设置组播端口
     *
     * @param multicastPort 组播端口
     */
    public void setMulticastPort(int multicastPort)
    {
        this.multicastPort = multicastPort;
    }
}
